using System;
using System.Collections.Generic;
using System.Text;
using PostSharp.Laos;
using System.Web.Services.Protocols;
using System.Reflection;

namespace AopLoadBalancer.Client
{
    [Serializable]
    public class LoadBalancedAttribute : OnMethodBoundaryAspect
    {
        private ClusterHosts clusterHosts;
        private readonly string[] balancedMethods = 
            new string[] {"System.String SimpleCall(System.String)",
                          "AopLoadBalancer.Sample.Client.TestService.ResponseMessage ComplexCall(AopLoadBalancer.Sample.Client.TestService.RequestMessage)"};

        private readonly string HeaderPropertyName = "AopLoadBalancer";


        private static void Log(String s)
        {
            Console.WriteLine("[AOP] " + s);
        }

        #region Aspects

        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            if (!IsLoadBalanceMethod(eventArgs.Method))
            {
                return;
            }
            //Log("Balancing " + eventArgs.Method.Name);

            SoapHttpClientProtocol client = (SoapHttpClientProtocol) eventArgs.Instance;

            if (clusterHosts == null)
            {
                clusterHosts = new ClusterHosts(client.Url);
            }

            string url = clusterHosts.GetNextUrl();
            Log("Using url: "+url);
            client.Url = url;
        }


        public override void OnException(MethodExecutionEventArgs eventArgs)
        {
            if (!IsLoadBalanceMethod(eventArgs.Method))
            {
                return;
            }
            //Log("Exception:" + eventArgs.Method + " " + eventArgs.Exception.Message);

            SoapHttpClientProtocol client = (SoapHttpClientProtocol) eventArgs.Instance;
            clusterHosts.Invalidate(client.Url);
        }


        public override void OnSuccess(MethodExecutionEventArgs eventArgs)
        {
            if (!IsLoadBalanceMethod(eventArgs.Method))
            {
                return;
            }


            //Log("Success:" + eventArgs.Method);

            String[] hostsList = ExtractHostListFromHeader(eventArgs.Instance);
            if (hostsList != null)
            {
                clusterHosts.UpdateHosts(hostsList);
            }
        }

        private string[] ExtractHostListFromHeader(object instance)
        {
            PropertyInfo property = instance.GetType().GetProperty(HeaderPropertyName);
            object availableHostsHeader = property.GetValue(instance, null);

            if (availableHostsHeader != null)
            {
                PropertyInfo hostsProprty = availableHostsHeader.GetType().GetProperty("Hosts");
                String[] receivedAvailableHostsList = (string[]) hostsProprty.GetValue(availableHostsHeader, null);

                return receivedAvailableHostsList;
            }
            return null;
        }

        #endregion

        private bool IsLoadBalanceMethod(MethodBase methodBase)
        {
            bool methodInArray = Array.IndexOf(balancedMethods, methodBase.ToString()) != -1;
            return methodInArray;
        }
    }
}